﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace PdfLibrary
{
    public class PdfXrefTable
    {
        public PdfXrefSection[] Sections;

        public PdfXrefTable()
        {
        }

        public void LoadFromReader(PdfReader reader)
        {
            var sections = new List<PdfXrefSection>();

            while (PdfXrefSection.CanLoadFromReader(reader))
            {
                reader.PositionCheck("reading XrefSections");

                var section = new PdfXrefSection();
                section.LoadFromReader(reader);
                sections.Add(section);
            }
            reader.PositionCheckReset();

            this.Sections = sections.ToArray();
        }
    }

    public class PdfXrefSection
    {
        public PdfXrefSubSection[] Subsections;

        public PdfXrefSection()
        {
        }

        public void LoadFromReader(PdfReader reader)
        {
            if (reader.ReadLine() != "xref")
                throw new PdfParsingException(reader, "XrefSection: xref starting tag not found!");

            var subsections = new List<PdfXrefSubSection>();
            while (PdfXrefSubSection.CanLoadFromReader(reader))
            {
                reader.PositionCheck("reading XrefSubSections");

                var subsection = new PdfXrefSubSection();
                subsection.LoadFromReader(reader);
                subsections.Add(subsection);
            }
            this.Subsections = subsections.ToArray();
        }

        public static bool CanLoadFromReader(PdfReader reader)
        {
            int originalPosition = reader.Position;

            bool result = reader.ReadLine() == "xref";

            reader.Position = originalPosition;

            return result;
        }
    }

    public class PdfXrefSubSection
    {
        public int FirstEntryIndex;
        public int EntryCount;

        public PdfXrefEntry[] Entries;

        public PdfXrefSubSection()
        {
        }

        public void LoadFromReader(PdfReader reader)
        {
            string[] parts = reader.ReadLine().Split(' ');

            if (parts.Length != 2)
                throw new PdfParsingException(reader, String.Format("XrefSubSection contains {0} parts instead of 2!", parts.Length));

            try { this.FirstEntryIndex = int.Parse(parts[0]); }
            catch (Exception e) { throw new PdfParsingException(reader, "XrefSubSection: First entry index int parsing error!", e); }

            try { this.EntryCount = int.Parse(parts[1]); }
            catch (Exception e) { throw new PdfParsingException(reader, "XrefSubSection: Entry count int parsing error!", e); }

            this.Entries = new PdfXrefEntry[this.EntryCount];

            for (int i = 0; i < this.EntryCount; i++)
            {
                var entry = new PdfXrefEntry() { ObjectNumber = FirstEntryIndex + i };

                entry.LoadFromReader(reader);

                this.Entries[i] = entry;
            }
        }

        public static bool CanLoadFromReader(PdfReader reader)
        {
            int originalPosition = reader.Position;

            bool result = ExtensionMethods.IsInt(reader.ReadWord()) && ExtensionMethods.IsInt(reader.ReadWord());

            reader.Position = originalPosition;

            return result;
        }
    }

    public class PdfXrefEntry
    {
        public int ByteOffset;
        public int GenerationNumber;
        public bool IsFree;

        public int ObjectNumber;

        public PdfXrefEntry()
        {
        }

        public void LoadFromReader(PdfReader reader)
        {
            string[] parts = reader.ReadLine().Split(new []{ ' ' }, StringSplitOptions.RemoveEmptyEntries);

            if (parts.Length != 3)
                throw new PdfParsingException(reader, String.Format("XrefEntry contains {0} parts instead of 3!", parts.Length));

            try { this.ByteOffset = int.Parse(parts[0]); }
            catch (Exception e) { throw new PdfParsingException(reader, "XrefEntry: Byte offset int parsing error!", e); }

            try { this.GenerationNumber = int.Parse(parts[1]); }
            catch (Exception e) { throw new PdfParsingException(reader, "XrefEntry: Generation number int parsing error!", e); }

            if (!new string[] { "n", "f" }.Contains(parts[2]))
                throw new PdfParsingException(reader, String.Format("XrefEntry: usage information not in ['n', 'f'] ({0})!", parts[2]));

            this.IsFree = parts[2] == "f";
        }
    }
}
